///////////////////////////////////////////////////////////////////////////
//  Brainfreeze - Yet another sokoban puzzle game, this time in 3D
//  Copyright (C) 2006 Scott MacDonald
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
///////////////////////////////////////////////////////////////////////////
// GameLevel.cpp - code definition for a sokoban game level
#include "StdAfx.h"
#include "GameLevel.h"

GameLevel::GameLevel(int width, int height, char* leveldef)
{
	//
	// make sure we have sane values for width and height
	//
	if (width < 0 && width > LEVEL_MAX_WIDTH )
	{
		throw new std::exception("Width of game level must be within sane bounds");
	}

	if( height < 0 && width > LEVEL_MAX_HEIGHT )
	{
		throw new std::exception("Height of game level must be within sane bounds");
	}

	//
	// allocate room for the level layout (location of walls)
	//
	level = new BYTE[width * height];

	//
	// parse the leveldef string, and build a new sokoban level
	//
	for( int i = 0; i < width * height; i++ )
	{
		char c = leveldef[i];
		bool spotHasWall = false;

		//
		// sanity - we should never hit a premature null
		//
		if( c == '\0' )
		{
			throw new std::exception("Unexpected null found in leveldef string");
		}

		//
		// quickly calculate the location of this spot
		//
		int x = i % width;
		int y = i / width;

		//
		// What object is in this position?
		//
		switch( c )
		{
			case SYM_WALL:
				spotHasWall = true;
				break;

			case SYM_EMPTY:
				break;

			case SYM_PLAYER:
				// register the fact that the player is at this position
				player = Player(Ogre::Vector3( x, y, 0 ) );
				break;

			case SYM_BOX:
				// add a box to the level at the current position
				boxes.insert(boxes.end(), MovableObject(Ogre::Vector3( x, y, 0 ) ) );
				break;

			case SYM_GOAL:
				// add a new goal
				goals.insert(goals.end(), MovableObject(Ogre::Vector3( x, y, 0 ) ) );
				break;

			default:
				throw new std::exception("Unexpected character found in leveldef string");
				break;
		}

		//
		// write layout for this spot
		//
		if( spotHasWall )
		{
			level[i] = ENTITY_WALL;
		}
		else
		{
			level[i] = ENTITY_FLOOR;
		}
	}
	
	
	//
	// set up the class properties (fairly straightforward)
	//
	name = "Untitled Sokoban Level";
	levelWidth = width;
	levelHeight = height;
}

GameLevel::~GameLevel(void)
{
	// TO-DO: fix this
	delete[] level;
}

///////////////////////////////////////////////////////////////////////////
// Return what type of tile is at the specified location
///////////////////////////////////////////////////////////////////////////
BYTE GameLevel::GetTileType(Ogre::Vector3 pos)
{
	// make sure the position is in bounds
	assert( (int)pos.x >= 0.0 && (int)pos.x < levelWidth );
	assert( (int)pos.y >= 0.0 && (int)pos.y < levelHeight );

	return level[(int)pos.y * levelWidth + (int)pos.x];
}

bool GameLevel::HasBox(Ogre::Vector3 pos)
{
	//
	// look for a box
	//
	for( int i = 0; i < (int)(boxes.size()); i++ )
	{
		if( boxes[i].GetPos() == pos )
		{
			return true;
		}
	}

	return false;
}

MovableObject& GameLevel::GetBox(Ogre::Vector3 pos)
{
	//
	// look for a box
	//
	for( int i = 0; i < (int)(boxes.size()); i++ )
	{
		if( boxes[i].GetPos() == pos )
		{
			return boxes[i];
		}
	}

	throw new std::exception("There is no box located at the specified location");
}

MovableObject& GameLevel::GetBox(int i )
{
	//
	// sanity
	//
	assert( i >= 0 && i < (int)(boxes.size()) );

	return boxes[i];
}

///////////////////////////////////////////////////////////////////////////
// Get the number of goals in this level
///////////////////////////////////////////////////////////////////////////
int GameLevel::GetGoalCount()
{
	return (int)goals.size();
}

///////////////////////////////////////////////////////////////////////////
// Get the position of a goal in this level
///////////////////////////////////////////////////////////////////////////
Ogre::Vector3 GameLevel::GetGoalPos(int index)
{
	assert( index >= 0 && index < goals.size() );

	return goals[index].GetPos();
}

MovableObject& GameLevel::GetGoal(int index )
{
//
	// sanity
	//
	assert( index >= 0 && index < (int)(goals.size()) );

	return goals[index];
}

///////////////////////////////////////////////////////////////////////////
// Get the number of boxes in this level
///////////////////////////////////////////////////////////////////////////
int GameLevel::GetBoxCount()
{
	return (int)boxes.size();
}

///////////////////////////////////////////////////////////////////////////
// Get the position of a box in this level
///////////////////////////////////////////////////////////////////////////
Ogre::Vector3 GameLevel::GetBoxPos(int index)
{
	assert( index >= 0 && index < boxes.size() );

	return boxes[index].GetPos();
}

///////////////////////////////////////////////////////////////////////////
// Checks if the position specified already has an object there.
///////////////////////////////////////////////////////////////////////////
bool GameLevel::IsSpaceOccupied(Ogre::Vector3 pos)
{
	//
	// is the player there?
	//
	if( player.GetPos() == pos )
	{
		return true;
	}

	//
	// are any boxes in the way?
	//
	for( int i = 0; i < (int) boxes.size(); i++ )
	{
		if( boxes[i].GetPos() == pos )
		{
			return true;
		}
	}

	//
	// is there a wall here?
	//
	if( level[PointTo1D(pos)] == ENTITY_WALL )
	{
		return true;
	}

	// nope, we're good
	return false;
}
